{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"images/strathsdr_banner.png\" align=\"left\" >"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# RFSoC OFDM Transceiver\n",
    "----\n",
    "\n",
    "<div class=\"alert alert-box alert-info\">\n",
    "Please use Jupyter Labs http://board_ip_address/lab for this notebook.\n",
    "</div>\n",
    "\n",
    "This notebook demonstrates the implementation of an Orthogonal Frequency Division Multiplexing (OFDM) transceiver on RFSoC. PYNQ is used to control the underlying modulation scheme of the OFDM sub-carriers and for visualisation of data at various stages in the transmit/receive chain, such as the received constellations.       \n",
    "\n",
    "## Aims \n",
    "* To demonstrate a complete OFDM transceiver.\n",
    "* Explain the various stages OFDM comprises.\n",
    "* Provide an interactive and responsive means to inspect the data at each stage.\n",
    "\n",
    "## Table of Contents\n",
    "* [Introduction](#introduction)\n",
    "    * [Hardware Setup](#hardware-setup)\n",
    "    * [Software Setup](#software-setup)\n",
    "* [Transmit](#transmit)\n",
    "    * [Symbol Generation](#symbol-gen)\n",
    "* [Receive](#creating-images)\n",
    "    * [Constellation Plot](#constellation-plot)\n",
    "* [Conclusion](#conclusion)\n",
    "\n",
    "## References\n",
    "* [Xilinx, Inc, \"USP RF Data Converter: LogiCORE IP Product Guide\", PG269, v2.4, November 2020](https://www.xilinx.com/support/documentation/ip_documentation/usp_rf_data_converter/v2_4/pg269-rf-data-converter.pdf)\n",
    "\n",
    "## Revision History\n",
    "* **v1.0** | 26/02/2021 | OFDM transceiver notebook\n",
    "* **v1.1** | 30/03/2022 | OFDM DUC and DDC change"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction <a class=\"anchor\" id=\"introduction\"></a>\n",
    "\n",
    "The demonstrator is a complete OFDM transceiver. This notebook will explain each stage of the system with a combination of text, diagrams and live data capture. [Figure 1](#fig-1) below provides an overview of the system.\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-1\"></a>\n",
    "<figure>\n",
    "<img src=\"./images/system_overview.png\" height='75%' width='75%'/>\n",
    "    <figcaption><b>Figure 1: OFDM Demonstrator System Overview</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "The OFDM system starts with generation of random data symbols from 1 of 10 possible modulation schemes (BPSK to 1024 QAM), based on input provided from PYNQ. In accordance with the procedure used in the IEEE 802.11a/g standard, the symbols are grouped into blocks of 48 for mapping to sub-carriers. The OFDM symbol consists of 48 data sub-carriers, 4 pilot sub-carriers and 12 null sub-carriers (including DC). The final OFDM symbol is created by performing a 64 point IFFT and adding a 16 sample Cyclic Prefix (CP). The transmitted signal consists of a continuous stream of OFDM symbols. At the very beginning of the data stream, the L-STF and L-LTF training symbols from the IEEE 802.11a/g standard are transmitted, to aid synchronisation and channel estimation tasks in the receiver. \n",
    "\n",
    "In the receiver, timing and frequency synchronisation are performed to acquire symbol timing and correct for any frequency offsets. Once timing is achieved, the FFT is performed to recover the underlying data symbols in each OFDM symbol. The L-LTF symbols are used to estimate the channel frequency response at each sub-carrier position, which subsequently allows the data to be equalised. Finally, the pilot symbols are used to correct for residual phase errors in the phase tracking stage. The recovered symbols are then passed into the PS for visualisation in PYNQ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Hardware Setup <a class=\"anchor\" id=\"hardware-setup\"></a>\n",
    "Your RFSoC4x2 development board should be setup in single channel mode. There are several SMA interfaces on your board that are labelled DAC A, DAC B, ADC A, and ADC B. To setup your board for this demonstration, you can connect a loopback channel as shown in [Figure 2](#fig-2).\n",
    "\n",
    "The default loopback configuration in this demonstration is connected as follows:\n",
    "* DAC A to ADC A\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-2\"></a>\n",
    "<figure>\n",
    "<img src='images/rfsoc4x2_setup.png' height='50%' width='50%'/>\n",
    "    <figcaption><b>Figure 2: RFSoC4x2 development board setup in loopback mode.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "The loopback connection will be useful for running the OFDM demonstrator. **Do Not** attach an antenna to any SMA interfaces labelled DAC A or DAC B.\n",
    "\n",
    "<div class=\"alert alert-box alert-danger\">\n",
    "<b>Caution:</b>\n",
    "    In this demonstration, we generate signals using the RFSoC development board. Your device should be setup in loopback mode. You should understand that the RFSoC platform can also transmit RF signals wirelessly. Remember that unlicensed wireless transmission of RF signals may be illegal in your geographical location. Radio signals may also interfere with nearby devices, such as pacemakers and emergency radio equipment. Note that it is also illegal to intercept and decode particular RF signals. If you are unsure, please seek professional support.\n",
    "</div>\n",
    "\n",
    "### Software Setup\n",
    "The setup for the OFDM demonstration system is nearly complete. The majority of the libraries used by the demonstrator design are contained inside the RFSoC-OFDM software package. We only need to run a few code cells to initialise the environment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rfsoc_ofdm.overlay import Overlay\n",
    "import ipywidgets as ipw\n",
    "\n",
    "ofdm_hw = Overlay()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transmit <a class=\"anchor\" id=\"transmit\"></a>\n",
    "\n",
    "### Symbol Generation <a class=\"anchor\" id=\"symbol-gen\"></a>\n",
    "There are a total of 10 modulation schemes available to be transmitted. These are generated on the programmable logic and can be chosen between by updating the *ofdm_tx IP core's* **mod** register with a value from 0-9 over AXI4-Lite. [Figure 3](#fig-3) illustrates the IP core as a simplified block diagram.\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-3\"></a>\n",
    "<figure>\n",
    "<img src=\"./images/symbol_generation.png\" height='45%' width='45%'/>\n",
    "    <figcaption><b>Figure 3: Symbol generation block diagram.</b></figcaption>\n",
    "</figure>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This drop down widget sends the value associated with each modulation scheme to the ofdm_tx core. Run the cell to use it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ofdm_hw.ofdm_transmitter.modulation_dropdown.get_widget()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The output of the symbol generation block has been tapped off, allowing for the live symbols to be visualised in Jupyter Lab. Run the cell below and hit play on the chart to inspect the symbols generated on the programmable logic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ipw.VBox([ofdm_hw.inspectors['transmitter'].time_plot(),\n",
    "          ofdm_hw.inspectors['transmitter'].plot_control()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Receive\n",
    "\n",
    "### Constellation Plot <a class=\"anchor\" id=\"constellation-plot\"></a>\n",
    "\n",
    "Run the cell below to see the output of the OFDM receiver displayed as a constellation: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ipw.VBox([ofdm_hw.inspectors['constellation'].constellation_plot(),\n",
    "          ofdm_hw.inspectors['constellation'].plot_control()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try changing the modulation scheme using the code cell that was ran earlier. You should be able to visualise the modulation schemes in the plot above."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Conclusion <a class=\"anchor\" id=\"conclusion\"></a>\n",
    "This notebook has demonstrated a live OFDM transceiver operating on an RFSoC. It has been shown how PYNQ can be used to interact with various parts of the hardware design, offering control of the system and visualisation of data. \n",
    "\n",
    "* The various components comprising the OFDM transmitter and receiver have been introduced at a high level. \n",
    "    * Modulation symbols were inspected.\n",
    "    * The received and sychronised constellation were plotted.\n",
    "* Interacted with a real-time RF system.\n",
    "    * Changed modulation scheme.\n",
    "\n",
    "----\n",
    "----"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
